namespace Jsl.FxCop
{
    using System;
    using Microsoft.FxCop.Sdk;

    /// <summary>
    /// Verifies that components such as WinForms and UserControls are localizable.
    /// </summary>
    public class ComponentsShouldBeLocalizable : BaseIntrospectionRule
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="ComponentsShouldBeLocalizable"/> class.
        /// </summary>
        public ComponentsShouldBeLocalizable()
            : base(typeof(ComponentsShouldBeLocalizable).Name, "Jsl.FxCop.Rules", typeof(ComponentsShouldBeLocalizable).Assembly)
        {
        }
        #endregion Constructors

        #region Public Methods
        /// <summary>
        /// Checks the specified method.
        /// </summary>
        /// <param name="member">The member to check.</param>
        /// <returns>The problems found if any.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods", Justification = "The parameters are already checked.")]
        public override ProblemCollection Check(Member member)
        {
            member.DoIfInitializeComponent(
                method =>
                {
                    // Find the creation of the ComponentResourceManager, if any.
                    var resourceManagerCreateIndex = FindComponentResourceManagerCreate(method.Instructions);
                    if (resourceManagerCreateIndex < 0)
                    {
                        // Didn't find the create of the resource manager so this must not be localizable.
                        this.Problems.Add(new Problem(this.GetResolution(), method.Instructions[0].SourceContext));
                    }
                    else
                    {
                        // Find a ComponentResourceManager.ApplyResources() method call, if any.
                        var applyResourcesIndex = FindApplyResources(method.Instructions, resourceManagerCreateIndex);
                        if (applyResourcesIndex < 0)
                        {
                            // Didn't find any ApplyResources calls so must not be localizable.
                            this.Problems.Add(new Problem(this.GetResolution(), method.Instructions[0].SourceContext));
                        }
                    }
                });

            return this.Problems;
        }
        #endregion Public Methods

        #region Private Methods
        private static int FindApplyResources(InstructionCollection instructions, int resourceManagerCreateIndex)
        {
            for (var instructionIndex = resourceManagerCreateIndex + 1; instructionIndex < instructions.Count; instructionIndex++)
            {
                var instruction = instructions[instructionIndex];
                if (instruction.OpCode == OpCode.Callvirt)
                {
                    var method = (Method)instruction.Value;
                    if (method.FullName == WellKnownTypeNames.ComponentResourceManager + ".ApplyResources(" + WellKnownTypeNames.Object + "," + WellKnownTypeNames.String + ")")
                    {
                        return instructionIndex;
                    }
                }
            }

            return -1;
        }

        private static int FindComponentResourceManagerCreate(InstructionCollection instructions)
        {
            if (instructions == null)
            {
                throw new ArgumentNullException("instructions");
            }

            for (var instructionIndex = 0; instructionIndex < instructions.Count; instructionIndex++)
            {
                var instruction = instructions[instructionIndex];
                if (instruction.OpCode == OpCode.Newobj)
                {
                    var initializer = (InstanceInitializer)instruction.Value;
                    if (initializer.FullName == WellKnownTypeNames.ComponentResourceManager + ".#ctor(" + WellKnownTypeNames.Type + ")")
                    {
                        return instructionIndex;
                    }
                }
            }

            return -1;
        }
        #endregion Private Methods
    }
}
